<?php
/**
 * This file is part of the Yacms (http://www.yacms.org)
 * Copyright (c) 2019 gylr0505 (http://www.yacms.org)
 *
 * Author: 鬼一浪人 <gylr0505@163.com>
 *
 * DateTime: 19-10-24 下午8:37
 * Description:
 */
 
declare(strict_types = 1);

namespace App\Template\Type;

use App\Template\Checker;
use App\Template\Log\Record;
use App\Template\ParamsBag;
use App\Template\Queue\ConsumerIntervalControl;
use App\Template\Queue\ProducerIntervalControl;
use App\Template\TemplateInterface;
use App\Template\TemplateSender;
use Swlib\Saber\Response;
use Swlib\SaberGM;
use Swoft\Redis\Redis;
use Swoft\Stdlib\Helper\ArrayHelper;

abstract class TemplateAbstract implements TemplateInterface
{
    /**
     * @var 模板消息类型
     */
    protected  $type;
    /**
     * @var int 当前模板消息主键id
     */
    protected  $id;

    /**
     * @param array $params
     * @return bool|mixed
     */
    public function accept($params = [])
    {
        if (!$params){
            return false;
        }
        $control = new ProducerIntervalControl($this,$params);
        return $control->isNext();
    }

    /**
     * 通知客户端，该方法
     * @param array $params
     * @return mixed|void
     */
    public function notifyClient($params = [])
    {
        $notify_url = $params['notifyUrl'] ?? '';
        if (!$notify_url){
            return;
        }
        //执行异步通知
        call_user_func([$this,'handleNotifyClientCallback'], SaberGM::post($notify_url,$params), $params);
    }

    /**
     * @param Response $result
     * @param array $params
     * @return mixed|void
     */
    public function handleNotifyClientCallback(Response $result, $params = [])
    {
        $content = $result->getBody()->getContents();
        if (!$content){
            return;
        }
        if (is_string($content)){
            $content = json_decode($content, true);
        }

        foreach ($content as $open_id => $item){
            if (is_array($item)){
                $params = ArrayHelper::merge($params, $item);
            }
            if (is_string($item)){
                $open_id = $item;
            }
            $params['open_id'] = $open_id;

            $this->push((new ParamsBag($params))->getParams());
        }
        $content = null;
    }

    /**
     * @return string
     */
    protected function generatorQueueKey()
    {
        return (string)($this).':'.$this->id;
    }

    /**
     * @param $value
     * @return mixed|void
     */
    public function push($value)
    {
        if (is_array($value)){
            $value = json_encode($value);
        }
        Redis::rPush($this->generatorQueueKey(),$value);
    }

    /**
     * @return mixed|void
     */
    public function pop()
    {
        $res =  Redis::lPop($this->generatorQueueKey());
        if ($res){
            return json_decode($res, true);
        }
        return null;
    }

    /**
     * @return mixed|void
     */
    public function clear()
    {
        Redis::del($this->generatorQueueKey());
    }

    /**
     * @param array $params
     * @return mixed|void
     * @throws \ReflectionException
     * @throws \Swoft\Bean\Exception\ContainerException
     * @throws \Swoft\Db\Exception\DbException
     */
    public function send($params = [])
    {
        while (1){
            if (!ConsumerIntervalControl::getInstance()->isNext($params['appId'] ?? '')){
                return false;
            }

            $param = $this->pop();
            if (!$param){
                return false;
            }
            //增加总量
            ConsumerIntervalControl::getInstance()->addCounterOnce($params['appId'] ?? '');

            $sender = new TemplateSender(new ParamsBag($params));

            call_user_func([$this,'handleSendCallback'], $sender->execute(), $params);
        }
    }

    /**
     * @param Response $result
     * @param array $params
     * @return bool|mixed
     */
    public function handleSendCallback(Response $result, $params = [])
    {
        $content = $result->getBody()->getContents();
        $checker = new Checker($content);
        $is_success = $checker->isSuccess();
        if (!$is_success){
            //发现失败，即终止后续的发送
            ConsumerIntervalControl::getInstance()->notifyFail($params['appId'] ?? '');
        }
        //记录日志...
        $log = new Record($params['id'] ?? 0, $is_success);
        $log->execute();
        $log = null;
        return $is_success;
    }


    /**
     * @return string
     */
    public function __toString()
    {
        return 'template_queue_prefix:'.$this->type;
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return 模板消息类型
     */
    public function getType()
    {
        return $this->type;
    }
}